#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <cstring>
#include <memory>

// 采用状态机，实现一个简单的TCP客户端重连效果
enum class Status
{
    NEW,
    CONNECTED,    // 连接成功或重连成功
    CONNETING,    // 正在连接
    DISCONNECTED, // 重连失败
    CLOSED        // 连接失败
};

enum
{
    SOCKET_ERR = 1
};

static const int default_retry_interval = 1; // 每隔一秒重连一次
static const int default_max_retries = 5;    // 五次
#define BUFFER_SIZE 1024

class ClientConnection
{
public:
    ClientConnection(const std::string &ip, uint16_t port)
        : _status(Status::NEW),
          _sockfd(-1),
          _port(port),
          _ip(ip),
          _retry_interval(default_retry_interval),
          _max_retries(default_max_retries)
    {
    }

    void Connect()
    {
        // 创建套接字，连接服务器
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            std::cerr << "socket error" << std::endl;
            exit(SOCKET_ERR);
        }

        // 2. connect
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(_port);
        inet_pton(AF_INET, _ip.c_str(), &server.sin_addr);
        // p:process(进程), n(网络) -- 不太准确，但是好记忆

        int n = connect(_sockfd, (struct sockaddr *)&server, sizeof(server));
        if (n < 0)
        {
            Disconnect(); // 关闭文件描述符资源
            _status = Status::DISCONNECTED;
            return;
        }

        // 连接成功
        _status = Status::CONNECTED;
    }

    // 断开连接
    void Disconnect()
    {
        // 如果sockfd创建成功，需要关闭
        if (_sockfd != -1)
        {
            close(_sockfd);
            _status = Status::CLOSED;
            _sockfd = -1;
        }
    }

    // 重连
    void Reconnect()
    {
        // 此时的状态为 正在连接中 该状态设置为可选
        _status = Status::CONNETING;
        int cnt = 0;
        while (cnt < _max_retries)
        {
            // 实际的操作是重新创建sockfd，进行connect
            Connect();
            ++cnt;
            if (_status == Status::CONNECTED)
            {
                std::cout << "客户端重连成功..." << std::endl;
                return;
            }
            std::cout << "重连次数: " << cnt << ", 最大上限: " << _max_retries << std::endl;
            // 重连时间间隔
            sleep(_retry_interval);
        }
        // 重连失败了
        std::cout << "客户端重连失败了" << std::endl;
        _status = Status::CLOSED;
    }

    void Process()
    {
        // 向服务器发送请求，并打印响应内容

        // 这里为了简化操作，直接向服务器发送特定消息
        std::string message = "Hello server~";
        char buffer[BUFFER_SIZE];
        while (true)
        {
            int sendBytes = send(_sockfd, message.c_str(), message.size(), 0);
            // 发送失败，关闭客户端
            if (sendBytes <= 0)
            {
                std::cout << "客户端异常, 发送请求失败." << std::endl;
                _status = Status::CLOSED;
                break;
            }
            else
            {
                // 向服务器发送请求成功

                // 接收消息
                int recvBytes = recv(_sockfd, buffer, BUFFER_SIZE - 1, 0);
                if (recvBytes > 0)
                {
                    buffer[recvBytes] = 0;
                    std::cout << "server echo-> " << buffer << std::endl;
                }
                else if (recvBytes == 0) // 服务器掉线了，需要进行重连
                {
                    _status = Status::DISCONNECTED;
                    break;
                }
                else
                {
                    std::cout << "客户端异常, 接收响应失败." << std::endl;
                    _status = Status::CLOSED;
                }
            }
            sleep(1);
        }
    }

    Status GetStatus()
    {
        return _status;
    }

    ~ClientConnection()
    {
        Disconnect();
    }

private:
    Status _status;
    int _sockfd;
    uint16_t _port;
    std::string _ip;
    int _retry_interval; // 重连时间间隔
    int _max_retries;    // 最大重连次数
};

class TCPClient
{
public:
    TCPClient(const std::string &ip, uint16_t port)
        : _connect(ip, port)
    {
    }

    void Execute()
    {
        while (true)
        {
            switch (_connect.GetStatus())
            {
            case Status::NEW:
                std::cout << "创建连接中..." << std::endl;
                _connect.Connect();
                break;
            case Status::CONNECTED:
                std::cout << "连接成功, 开始执行任务." << std::endl;
                _connect.Process();
                break;
            case Status::CLOSED:
                _connect.Disconnect();
                std::cout << "连接失败, 退出" << std::endl;
                return;
            case Status::DISCONNECTED:
                std::cout << "网络异常, 开始重连..." << std::endl;
                _connect.Reconnect();
                break;
            default:
                break;
            }
        }
    }

private:
    ClientConnection _connect;
};

void Usage(const std::string &process)
{
    std::cout << "Usage: " << process << " server_ip server_port" << std::endl;
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        return 1;
    }
    std::string server_ip = argv[1];
    uint16_t server_port = std::stoi(argv[2]);

    std::unique_ptr<TCPClient> client = std::make_unique<TCPClient>(server_ip, server_port); 
    client->Execute();

    return 0;
}
